﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using CodeParser.Expressions;
using CodeParser.Tokens;
using System.Xml.Serialization;

namespace CodeParser
{
    public class Parser
    {

        private readonly Language _language;

        public Parser()
        {
            _language = new Language
                            {
                                TokenParsers = new List<TokenParser>()
                                                   {
                                                       // do not change sequence: tuned for performance (most common tokentypes first = least negative token tests)
                                                       new HlslSingleChars(),
                                                       new Precompiler(),
                                                       new Identifier(),
                                                       new Float(),
                                                       new Keywords(TokenType.Operator, "==", "+=", "-=", "++", "--",
                                                                    "*=", "/=", "%=", "!=", "&&", "||", "<<=", ">>=", 
                                                                    "<<", ">>", "<=", ">=", "&=", "|=", "^="),
                                                       new Keychars(TokenType.Operator, '=', '<', '>', '+', '-', '/',
                                                                    '*', '%', '!', '~', '&', '|', '^'),
                                                       new SingleChar(TokenType.Point, '.'),
                                                       // a float can start with a ., so float check must be before dot check
                                                       new Int(),
                                                       new StringBlock(TokenType.CommentBlock, "/*", "*/") {RemoveTokens = true},
                                                       new StringBlock(TokenType.CommentLine, "//", "\n") {RemoveTokens = true},
                                                       new CharBlock(TokenType.LiteralString, '"', '"', '\\', '\r')
                                                   },
                                DocumentParser = new DocumentParser()
                            };
            var techniqueParser = new TechniqueParser();
            var techniqueCodeBlockParser = new CodeBlockParser();
            var functionCallParser = new FunctionCallParser();
            var passParser = new PassParser();
            var passCodeBlockParser = new CodeBlockParser();
            var functionCodeBlockParser = new CodeBlockParser();
            var argumentListParser = new ArgumentListParser();
            var functionDefinitionParser = new FunctionDefinitionParser();
            var annotationParser = new AnnotationParser();
            var structParser = new StructParser();
            var structCodeBlockParser = new CodeBlockParser();

            _language.DocumentParser.Parsers.Add(techniqueParser);
            _language.DocumentParser.Parsers.Add(structParser);
            _language.DocumentParser.Parsers.Add(functionDefinitionParser);
            techniqueParser.CodeBlockParser = techniqueCodeBlockParser;
            techniqueParser.AnnotationParser = annotationParser;
            techniqueCodeBlockParser.BodyParsers.Add(functionCallParser);
            techniqueCodeBlockParser.BodyParsers.Add(passParser);
            passParser.CodeBlockParser = passCodeBlockParser;
            passCodeBlockParser.BodyParsers.Add(functionCallParser);
            functionCodeBlockParser.BodyParsers.Add(functionCallParser);
            functionCallParser.ArgumentListParsers.Add(argumentListParser);
            argumentListParser.ArgumentParsers.Add(functionCallParser);
            functionDefinitionParser.ArgumentListParsers.Add(argumentListParser);
            functionDefinitionParser.CodeBlockParsers.Add(functionCodeBlockParser);
            structParser.CodeBlockParser = structCodeBlockParser;
        }

        public ParseResult Parse(string source)
        {
            var treeParser = new TreeParser(_language);
            var result = new ParseResult
                             {
                                 Tokens = Tokenizer.GetTokens(_language.TokenParsers, source.ToLower())
                             };
            result.TreeRoot = treeParser.Parse(result.Tokens.ToArray());
            return result;
        }

    }
}
